package it.ipermemory;

import it.ipermemory.graphic.CustomPopup;
import it.ipermemory.graphic.ProgressDialogCustom;
import it.ipermemory.logic.Thing;
import it.ipermemory.logic.Game;
import it.ipermemory.utilities.MediaControllerPlayer;
import it.ipermemory.utilities.NetworkService;

import java.io.IOException;

import android.app.Activity;
import android.graphics.Typeface;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.media.MediaPlayer.OnVideoSizeChangedListener;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView;

/**
 * Activity that takes care of reproducing a video with the classic management
 * buttons.
 * 
 * @author Gianluca Carlesso (gianluca.carlesso@gmail.com)
 * @version 1.0
 */
public class VideoActivity extends Activity implements
		OnBufferingUpdateListener, OnCompletionListener, OnPreparedListener,
		OnVideoSizeChangedListener, SurfaceHolder.Callback {

	/**
	 * ProgressDialog to display to put the user on hold while running
	 * background processing
	 */
	private ProgressDialogCustom prgbar = null;

	/**
	 * Width of the video
	 */
	private int mVideoWidth;

	/**
	 * Height of the video
	 */
	private int mVideoHeight;

	/**
	 * Object MediaPlayer that manage a multimedia content
	 */
	private MediaPlayer mMediaPlayer;

	/**
	 * Viewing area where you can view the video. Graphic object
	 */
	private SurfaceView mPreview;

	/**
	 * Manager of SurfaceView
	 */
	private SurfaceHolder holder;

	/**
	 * Thing on which to display the video
	 */
	private Thing thing;

	/**
	 * Object to display a pop-up
	 */
	private CustomPopup cp = null;

	/**
	 * Object that manage the playback
	 */
	private MediaControllerPlayer mControllerPlayer;

	/**
	 * Object that takes care of downloading the video in the background, that
	 * is, it performs the buffering
	 */
	private BuildVideoTask task = null;

	/**
	 * Flag that indicates that you can not retrieve the video size
	 */
	private boolean mIsVideoSizeKnown = false;

	/**
	 * Flag that indicates that the video is ready to play
	 */
	private boolean mIsVideoReadyToBePlayed = false;

	/**
	 * Flag that indicates that the video is playing in his first
	 */
	private boolean isNew = true;

	/**
	 * An object that initializes the video screen
	 */
	@Override
	public void onCreate(Bundle icicle) {
		super.onCreate(icicle);
		setContentView(R.layout.activity_video);

		// Initialize MediaPlayer and sound
		int thing_id = getIntent().getExtras().getInt(Game.GAME_THING_CARD);

		thing = Game.parserXML.getThingById(thing_id);

		if (thing == null) {
			// It was not possible to retrieve the information necessary Thing,
			// show e message error
			mexError();
		} else {
			// Set the progress dialog
			prgbar = new ProgressDialogCustom(VideoActivity.this);
			prgbar.setBackgroundDrawable(getResources().getDrawable(
					R.drawable.background));

			// Prepare the graphics area for displaying the video
			mPreview = (SurfaceView) findViewById(R.id.surface);

			holder = mPreview.getHolder();
			holder.addCallback(VideoActivity.this);

			// Initialize the pop up errors
			cp = new CustomPopup(VideoActivity.this,
					getString(R.string.no_network_connection)) {

				@Override
				public void onClick(View v) {
					if (v == getYesButton()) {
						mPreview.setVisibility(View.GONE);
						dismiss();
						VideoActivity.this.finish();
					}
				}
			};
			cp.getNoButton().setVisibility(View.GONE);
			cp.setBackgroundDrawable(getResources().getDrawable(
					R.drawable.background));

			// Customize font
			Typeface typface = Typeface.createFromAsset(getAssets(),
					"fonts/wayoshi.ttf");
			((TextView) findViewById(R.id.total_time)).setTypeface(typface);
			((TextView) findViewById(R.id.current_time)).setTypeface(typface);
		}
	}

	/**
	 * Method that starts playing the video
	 * 
	 * @throws IOException
	 *             occurs when the source software presents problems for the
	 *             recovery of the video, or at the stage of preparation of the
	 *             video
	 */
	private void playVideo() throws IOException {
		// Reset the parameters of the currently playing video
		doCleanUp();

		// Create a new media player and set the listeners
		mMediaPlayer = new MediaPlayer();
		mMediaPlayer.setDataSource(thing.getVideo());

		mMediaPlayer.setDisplay(holder);

		// Set the various listeners who will act whichever occurs
		mMediaPlayer.setOnBufferingUpdateListener(VideoActivity.this);
		mMediaPlayer.setOnCompletionListener(VideoActivity.this);
		mMediaPlayer.setOnPreparedListener(VideoActivity.this);
		mMediaPlayer.setOnVideoSizeChangedListener(VideoActivity.this);
		mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

		// Initialize the controller for personalized video
		mControllerPlayer = new MediaControllerPlayer(mMediaPlayer,
				(ImageButton) findViewById(R.id.play_button),
				(ImageButton) findViewById(R.id.pause_button),
				(ImageButton) findViewById(R.id.stop_button),
				(TextView) findViewById(R.id.total_time),
				(TextView) findViewById(R.id.current_time));

		mMediaPlayer.prepare();
	}

	/**
	 * Method that is invoked when you update buffering
	 * 
	 * @param arg0
	 *            MediaPlayer that manage video
	 * @param percent
	 *            rate of progress of the buffering
	 */
	public void onBufferingUpdate(MediaPlayer arg0, int percent) {
		// Check Internet connection
		if (!NetworkService.checkNetworkConnection(getApplicationContext())) {
			// There is no network connection, display an error message
			if (!cp.isShowing()) {
				TextView message = new TextView(getApplicationContext());
				message.setText(getResources().getString(
						R.string.error_media_load));
				cp.setTextMessage(message);
				cp.show();
			}
		}

		// Update current time
		if (mControllerPlayer != null)
			mControllerPlayer.setCurrentTime();
	}

	/**
	 * Method that is invoked at the completion of the video playback. It must
	 * stop play and reset the custom controller
	 * 
	 * @param arg0
	 *            MediaPlayer of video
	 */
	public void onCompletion(MediaPlayer arg0) {
		try {
			mControllerPlayer.stopMedia();
		} catch (IllegalStateException e) {
			mexError();
		}
	}

	/**
	 * The method is called as soon as you retrieve the video information. In
	 * this way it is possible to set the size of the display area depending on
	 * the size of the video.
	 * 
	 * @param mp
	 *            MediaPlayer of video
	 * @param width
	 *            of video
	 * @param height
	 *            of video
	 */
	public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
		if (width == 0 || height == 0) {
			// A video size is zero, I get a error message
			mexError();
		} else {

			// Prepare the play area with the measures of the video and hide the
			// progress dialog
			mIsVideoSizeKnown = true;
			mVideoWidth = width;
			mVideoHeight = height;
			if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {
				holder.setFixedSize(mVideoWidth, mVideoHeight);
				if (isNew) {
					isNew = false;
					mControllerPlayer.playPause();
				}
				mControllerPlayer.setEnabledPlayButton(true);
				mControllerPlayer.setCurrentTime();

				prgbar.dismiss();
			}
		}
	}

	/**
	 * The method is called when the video is ready.
	 * 
	 * @param mediaplayer
	 *            MediaPlayer of video
	 */
	public void onPrepared(MediaPlayer mediaplayer) {
		mIsVideoReadyToBePlayed = true;
		// Prepare the play area with the measures of the video and hide the
		// progress dialog
		if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {
			if (isNew) {
				isNew = false;
				mControllerPlayer.playPause();
				mControllerPlayer.setTotalTime();
			}
			mControllerPlayer.setEnabledPlayButton(true);
			mControllerPlayer.setCurrentTime();
		}
	}

	/**
	 * The method is called as soon as the video display area changes are made
	 * 
	 * @param surfaceholder
	 *            manager of the display
	 * @param i
	 *            format of video
	 * @param j
	 *            width of video
	 * @param k
	 *            heigth of video
	 */
	public void surfaceChanged(SurfaceHolder surfaceholder, int i, int j, int k) {

	}

	/**
	 * This method is called when the area of ​​the video display is destroyed
	 * 
	 * @param surfaceholder
	 *            manager of the display
	 */
	public void surfaceDestroyed(SurfaceHolder surfaceholder) {

	}

	/**
	 * The method is called when the display area of the video is created. In
	 * the specific case, this starts the preparation of video
	 * 
	 * @param holder
	 *            manager of the display
	 */
	public void surfaceCreated(SurfaceHolder holder) {
		// Create media video in async mode
		if (!NetworkService.checkNetworkConnection(getApplicationContext())) {
			// No Internet connection, show a message error
			cp.show();
		} else {
			// Starts the preparation of video
			prgbar.show();
			task = new BuildVideoTask();
			task.execute();
		}

	}

	/**
	 * Destroys the activity and releases the resources occupied by the media
	 */
	@Override
	protected void onDestroy() {
		super.onDestroy();

		// Hide progress dialog
		if (prgbar != null && prgbar.isShowing())
			prgbar.dismiss();

		// Hide message error
		if (cp != null && cp.isShowing()) {
			cp.dismiss();
		}

		// Stop buffering task
		if (task != null) {
			task.cancel(true);
		}

		// Release memory of memory
		if (mControllerPlayer != null)
			mControllerPlayer.releaseMedia();
	}

	/**
	 * Reset the video information
	 */
	private void doCleanUp() {
		mVideoWidth = 0;
		mVideoHeight = 0;
		mIsVideoReadyToBePlayed = false;
		mIsVideoSizeKnown = false;
	}

	/**
	 * This class has the task of preparing the video in the background
	 * 
	 * @author Gianluca Carlesso (gianluca.carlesso@gmail.com)
	 * @version 1.0
	 */
	public class BuildVideoTask extends AsyncTask<Void, Void, Exception> {

		/**
		 * The method is called to start processing the request
		 */
		@Override
		protected Exception doInBackground(Void... params) {
			try {
				playVideo();
			} catch (IllegalArgumentException e) {
				return e;
			} catch (SecurityException e) {
				return e;
			} catch (IllegalStateException e) {
				return e;
			} catch (IOException e) {
				return e;
			}

			return null;
		}

		/**
		 * The method is called the termination of the work in the background
		 * 
		 * @param result
		 *            returns an error that may occur in the development
		 */
		protected void onPostExecute(Exception result) {
			if (result != null)
				mexError();
		}
	}

	/**
	 * Play/Pause the video
	 * 
	 * @param view
	 *            object on which the click was performed
	 */
	public void playPauseVideo(View view) {
		mControllerPlayer.playPause();
	}

	/**
	 * Stop playing the video
	 * 
	 * @param view
	 *            object on which the click was performed
	 */
	public void stopVideo(View view) {
		try {
			mControllerPlayer.stopMedia();
		} catch (IllegalStateException e) {
			mexError();
		}
	}

	/**
	 * Show a pop-up error
	 */
	private void mexError() {
		cp = new CustomPopup(VideoActivity.this, getResources().getString(
				R.string.error_media_load)) {

			@Override
			public void onClick(View v) {
				this.dismiss();
				finish();
			}
		};

		if (prgbar != null && prgbar.isShowing()) {
			// Hide progress dialog before show the message error
			prgbar.dismiss();
		}

		cp.getNoButton().setVisibility(View.GONE);
		cp.show();
		cp.setBackgroundDrawable(getResources().getDrawable(
				R.drawable.background));
	}
}
